using Glpt.Data;
using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Data;

namespace Glpt.Task
{
    /// <summary>
    /// MongoDB数据流类
    /// </summary>
    public class MongoDBDataReader : IDataReader
    {
        private IEnumerator<BsonDocument> result = null;
        private Dictionary<string, int> columnsByName = new Dictionary<string, int>();
        private Dictionary<int, string> columnsByOrdinal = new Dictionary<int, string>();
        private Dictionary<string, string> colnames_ext = new Dictionary<string, string>();//附加列
        private Dictionary<string, string> colnames_sep = new Dictionary<string, string>();//特殊附加列
        private int Cols_ext = 0; //附加列的个数
        private BsonDocument currentRow;
        private bool _isClosed = true;
        private bool cur_read = false;
        private bool isEnd = false;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="dbi">数据库连接</param>
        /// <param name="table_name">表名</param>
        /// <param name="param">参数</param>
        public MongoDBDataReader(DbItem dbi, string table_name, Dictionary<string, string> param)
        {
            string TABLE_NAME = table_name;
            if (param.ContainsKey("TABLE_NAME") && param["TABLE_NAME"].Length > 0)
                TABLE_NAME = param["TABLE_NAME"];
            string FILTER = "";
            if (param.ContainsKey("FILTER") && param["FILTER"].Length > 0)
                FILTER = param["FILTER"];

            foreach (string key in param.Keys)
            {
                if (key.StartsWith(":"))
                {
                    colnames_ext.Add(key.Substring(1), param[key]);
                }
            }
            string connectionStr = AbstractInterFace.ReplaceString(dbi.GetConnString(), AbstractInterFace.PublicLeftChar, AbstractInterFace.PublicRightChar);
            MongoClient client = new MongoClient(connectionStr);
            IMongoDatabase database = client.GetDatabase(dbi.DB_NAME);
            IMongoCollection<BsonDocument> collection = database.GetCollection<BsonDocument>(TABLE_NAME);
            FilterDefinition<BsonDocument> filter = GetFilterDefinition(FILTER);
            IAsyncCursor<BsonDocument> collects = collection.FindSync<BsonDocument>(filter);
            if (collects.MoveNext())
            {
                result = collects.Current.GetEnumerator();
                _isClosed = false;
                cur_read = Read();
                if (cur_read)
                {
                    Cols_ext = 0;
                    foreach (string key in colnames_ext.Keys)
                    {
                        columnsByName.Add(key, Cols_ext);
                        columnsByOrdinal.Add(Cols_ext, key);
                        Cols_ext++;
                    }
                    int i = 0;
                    foreach (BsonElement ele in currentRow.Elements)
                    {
                        columnsByName.Add(ele.Name, i + Cols_ext);
                        columnsByOrdinal.Add(i + Cols_ext, ele.Name);
                        i++;
                    }
                }
            }
            else
                isEnd = true;
        }

        public bool Read()
        {
            if (isEnd) return false;
            if (cur_read)
            {
                cur_read = false;
            }
            else
            {
                if (result.MoveNext())
                {
                    currentRow = result.Current;
                }
                else
                {
                    isEnd = true;
                    return false;
                }
            }
            return true;
        }

        private FilterDefinition<BsonDocument> GetFilterDefinition(string FILTER)
        {
            FilterDefinitionBuilder<BsonDocument> filterBuilder = Builders<BsonDocument>.Filter;
            FilterDefinition<BsonDocument> filter = filterBuilder.Empty;
            if (FILTER.Length > 0)
            {
                FilterDefinition<BsonDocument> fi_or = filterBuilder.Empty;
                var filters_or = new List<FilterDefinition<BsonDocument>>();
                string[] filter_or = FILTER.Split(new string[] { " OR " }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string str_or in filter_or)
                {
                    var filters_and = new List<FilterDefinition<BsonDocument>>();
                    string[] filter_and = str_or.Split(new string[] { " AND " }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string str_and in filter_and)
                    {
                        Dictionary<string, string> param = DataFuns.SplitStringValue(str_and, ',');
                        foreach (string field in param.Keys)
                        {
                            var filterRegex = filterBuilder.Eq(field, param[field]);
                            filters_and.Add(filterRegex);
                        }
                    }
                    filters_or.Add(Builders<BsonDocument>.Filter.And(filters_and));
                }
                filter = Builders<BsonDocument>.Filter.Or(filters_or);
            }
            return filter;
        }

        /// <summary>
        /// 关闭StreamReader对象和基础流
        /// </summary>
        public void Close()
        {
            _isClosed = true;
        }

        public int FieldCount
        {
            get { return columnsByName.Count; }
        }
        public string GetString(int i)
        {
            if (i < Cols_ext)
                return colnames_ext[columnsByOrdinal[i]];
            else
                return currentRow.GetValue(columnsByOrdinal[i]).ToString();
        }

        public object GetValue(int i)
        {
            if (i < Cols_ext)
                return colnames_ext[columnsByOrdinal[i]];
            else
                return currentRow.GetValue(columnsByOrdinal[i]).ToString();
        }

        public string GetName(int i)
        {
            return columnsByOrdinal[i];
        }

        public int GetOrdinal(string name)
        {
            return columnsByName[name];
        }

        public int Depth
        {
            get
            {
                return 0;
            }
        }

        public bool IsClosed
        {
            get
            {
                return _isClosed;
            }
        }

        public int RecordsAffected
        {
            get
            {
                return -1;
            }
        }

        public DataTable GetSchemaTable()
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("ColumnName", typeof(string));
            dt.Columns.Add("ColumnOrdinal", typeof(int));
            dt.Columns.Add("ColumnSize", typeof(int)).DefaultValue = 255;
            dt.Columns.Add("NumericPrecision", typeof(short)).DefaultValue = 255;
            dt.Columns.Add("NumericScale", typeof(short)).DefaultValue = 255;
            dt.Columns.Add("IsUnique", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("IsKey", typeof(bool));
            dt.Columns.Add("BaseServerName", typeof(string));
            dt.Columns.Add("BaseCatalogName", typeof(string));
            dt.Columns.Add("BaseColumnName", typeof(string));
            dt.Columns.Add("BaseSchemaName", typeof(string));
            dt.Columns.Add("BaseTableName", typeof(string));
            dt.Columns.Add("DataType", typeof(System.Type));
            dt.Columns.Add("AllowDBNull", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("ProviderType", typeof(int)).DefaultValue = 22;
            dt.Columns.Add("IsAliased", typeof(bool));
            dt.Columns.Add("IsExpression", typeof(bool));
            dt.Columns.Add("IsIdentity", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("IsAutoIncrement", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("IsRowVersion", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("IsHidden", typeof(bool));
            dt.Columns.Add("IsLong", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("IsReadOnly", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("ProviderSpecificDataType", typeof(System.Type));
            dt.Columns.Add("DataTypeName", typeof(string)).DefaultValue = "string";
            dt.Columns.Add("XmlSchemaCollectionDatabase", typeof(string));
            dt.Columns.Add("XmlSchemaCollectionOwningSchema", typeof(string));
            dt.Columns.Add("XmlSchemaCollectionName", typeof(string));
            dt.Columns.Add("UdtAssemblyQualifiedName", typeof(string)).DefaultValue = 22;
            dt.Columns.Add("NonVersionedProviderType", typeof(int));
            dt.Columns.Add("IsColumnSet", typeof(bool)).DefaultValue = false;
            for (int i = 0; i < FieldCount; i++)
            {
                DataRow dr = dt.NewRow();
                dr["ColumnName"] = GetName(i);
                dr["ColumnOrdinal"] = i;
                dr["BaseColumnName"] = GetName(i);
                dr["DataType"] = typeof(string);
                dr["ProviderSpecificDataType"] = typeof(string);
                dt.Rows.Add(dr);
            }
            return dt;
        }

        public bool NextResult()
        {
            return false;
        }

        public object this[int i]
        {
            get
            {
                return GetValue(i);
            }
        }
        public object this[string name]
        {
            get
            {
                return GetValue(columnsByName[name]);
            }
        }
        public bool GetBoolean(int i)
        {
            return bool.Parse(GetString(i));
        }
        public byte GetByte(int i)
        {
            return byte.Parse(GetString(i));
        }
        public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
        {
            return -1;
        }
        public char GetChar(int i)
        {
            return char.Parse(GetString(i));
        }
        public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
        {
            return -1;
        }
        public IDataReader GetData(int i)
        {
            return null;
        }
        public string GetDataTypeName(int i)
        {
            return "String";
        }
        public DateTime GetDateTime(int i)
        {
            return DateTime.Parse(GetString(i));
        }
        public decimal GetDecimal(int i)
        {
            return decimal.Parse(GetString(i));
        }
        public double GetDouble(int i)
        {
            return double.Parse(GetString(i));
        }
        public Type GetFieldType(int i)
        {
            return typeof(string);
        }
        public float GetFloat(int i)
        {
            return float.Parse(GetString(i));
        }
        public Guid GetGuid(int i)
        {
            return Guid.Parse(GetString(i));
        }
        public short GetInt16(int i)
        {
            return short.Parse(GetString(i));
        }
        public int GetInt32(int i)
        {
            return int.Parse(GetString(i));
        }
        public long GetInt64(int i)
        {
            return long.Parse(GetString(i));
        }
        public int GetValues(object[] values)
        {
            for (int i = 0; i < columnsByName.Count; i++)
                values[i] = GetValue(i);
            return columnsByName.Count;
        }
        public bool IsDBNull(int i)
        {
            if (GetValue(i) == null)
                return true;
            else
                return false;
        }
        public void Dispose()
        {
            try
            {
                GC.Collect();
            }
            catch { }
        }
        ~MongoDBDataReader()
        {
            Dispose();
        }
    }
}
